home *** CD-ROM | disk | FTP | other *** search
- /*
- File: AIFF_writer.c
-
- Contains: Routines for dispatching a sound hardware output ('sdev') component.
-
- Written by: Mark Cookson--based on code by Kip Olson
-
- Copyright: Copyright © 1993-1999 by Apple Computer, Inc., All Rights Reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- Change History (most recent first):
- 8/16/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1
-
-
- */
- #include <Memory.h>
- #include <Errors.h>
- #include <Sound.h>
- #include <SoundInput.h>
- #include <Components.h>
- #include <Timer.h>
- #include <FixMath.h>
- #include <Files.h>
- #include <Script.h>
- #include <SoundComponents.h>
-
- #ifndef __AIFFWRITER__
- #include "AIFF_writer.h"
- #endif
-
- #ifndef __COMPONENTPROTOTYPES__
- #include "ComponentPrototypes.h"
- #endif
-
- #ifndef __HARDWARE__
- #include "Hardware.h"
- #endif
-
- #ifndef __STRUCTURES__
- #include "Structures.h"
- #endif
-
- #define DEBUG false
- #define FULLDEBUG false
-
- //This is needed for Think C, Metrowerks has the cool :__a1 operator
- //Ptr GetRegisterA1(void) = {0x2009}; /* MOVE.L A1,D0 */
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Main component Entry Point
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- #if !GENERATINGPOWERPC
- //----------------------------------------------------------------------------------
- // main
- //----------------------------------------------------------------------------------
- pascal ComponentResult main( ComponentParameters *params, SoundComponentGlobalsPtr globals );
- pascal ComponentResult main( ComponentParameters *params, SoundComponentGlobalsPtr globals ) {
- return (SoundComponentEntryPoint(params,globals));
- }
- #endif
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Component Manager Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- /* ==============================================================================
- SoundComponentOpen
-
- This routine is called when the Component Manager creates an instance of this
- component. The routine should allocate global variables in the appropriate heap
- and call SetComponentInstanceStorage() so the Component Manager can remember
- the globals and pass them to all the method calls.
-
- Determining the heap to use can be tricky. The Component Manager will normally
- load the component code into the system heap, which is good, since many applications
- will be sharing this component to play sound. In this case, the component's global
- variable storage should also be created in the system heap.
-
- However, if system heap memory is tight, the Component Manager will load
- the component into the application heap of the first application that plays sound.
- When this happens, the component should create global storage in the application heap
- instead. The Sound Manager will make sure that other applications will not try
- to play sound while the component is in this application heap.
-
- To determine the proper heap to use, call GetComponentInstanceA5(). If the value
- returned is 0, then the component was loaded into the system heap, and all storage
- should be allocated there. If the value returned is non-zero, the component is in
- the application heap specifed by returned A5 value, and all storage should be
- allocated in this application heap.
-
- NOTE: If the component is loaded into the application heap, the value returned by
- GetComponentRefCon() will be 0.
- NOTE: Do not attempt to initialize or access hardware in this call, since the
- Component Manager will call SoundComponentOpen() BEFORE calling RegisterComponent().
- Instead, initialize the hardware during InitOutputDevice(), described below.
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __SoundComponentOpen(void *unused1, ComponentInstance self) {
- #pragma unused (unused1)
-
- Handle h;
- SoundComponentGlobalsPtr globals;
- long a5 = GetComponentInstanceA5(self); // find out if we were loaded in app heap or system heap
- ComponentResult result = noErr;
-
- #if DEBUG
- DebugStr ("\pIn __SoundComponentOpen;g");
- #endif
-
- h = NewHandleLockClear(sizeof(SoundComponentGlobals), a5 == 0); // get space for globals in appropriate heap
- if (h != nil) {
- globals = (SoundComponentGlobalsPtr) *h;
- globals->globalsHandle = h; // remember the handle
- globals->inSystemHeap = (a5 == 0); // remember which heap we are in
-
- globals->hwGlobals = GetHardwareGlobals(self, globals->inSystemHeap); // get hardware globals
-
- #ifdef FakeInterrupts
- gGlobals = globals;
- #endif
-
- SetComponentInstanceStorage (self, (Handle) globals); // save pointer to our globals
- } else {
- result = MemError();
- }
-
- #if DEBUG
- if (result != noErr) {
- DebugStr ("\pGot an error in __SoundComponentOpen");
- }
- #endif
-
- return (result);
- }
-
- /* ==============================================================================
- SoundComponentClose
-
- This routine is called when the Component Manager is closing the instance of
- this component. The routine should make sure all remaining data is written
- to the hardware and that the hardware is completely turned off. It should
- delete all global storage and close any other components that were opened.
-
- NOTE: Be sure to check that the globals pointer passed in to this routine is
- not set to NIL. If the SoundComponentOpen() routine fails for any reason, the Component
- Manager will call this routine passing in a NIL for the globals.
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __SoundComponentClose(SoundComponentGlobalsPtr globals, ComponentInstance self) {
- #if DEBUG
- DebugStr ("\pIn __SoundComponentClose;g");
- #endif
-
- if (globals) { // we have some globals
- ReleaseHardware(globals); // make sure the hardware is off and release it
-
- if (globals->sourceComponent)
- CloseMixerSoundComponent(globals->sourceComponent); // close mixer
-
- SaveHardwareGlobals(self, globals->hwGlobals); // save preferences
-
- DisposeHandle(globals->globalsHandle); // torch our storage
- }
-
- return (noErr);
- }
-
- /* ==============================================================================
- SoundComponentRegister
-
- This routine is called once, usually at boot time, when the Component Manager
- is first registering this component. This routine should check to see if the proper
- hardware is installed and return 0 if it is. If the hardware is not installed,
- the routine should return 1 and this component will not be registered. This is
- also an opportunity to do one-time initializations and perhaps register this
- component again if more than one hardware device is available. Global state information
- can also be saved in the component refcon by calling SetComponentRefCon();
-
- NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the
- component in order for this routine to be called.
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __SoundComponentRegister(SoundComponentGlobalsPtr globals) {
- #pragma unused (globals)
- NumVersion installedVersion;
- ComponentResult result;
-
- #if DEBUG
- DebugStr ("\pIn __SoundComponentRegister;g");
- #endif
-
- // Check for hardware here. We are always installed, so we return 0
-
- // we can only run if version 3.0 or greater of the sound manager is running
- // we can check the entire long because the format of NumVersion is BCD data
-
- installedVersion = SndSoundManagerVersion();
- if (installedVersion.majorRev >= 3) { // has 3.0 sound manager
- result = kComponentWantsToRegister;
- } else {
- result = kComponentDoesNotWantToRegister;
- }
-
- #if DEBUG
- if (result != noErr) {
- DebugStr ("\pGot an error in __SoundComponentRegister");
- }
- #endif
-
- return result;
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Standard Component Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- /* ==============================================================================
- InitOutputDevice
-
- This routine is called once when the Sound Manager first opens this component.
- The routine should initialize the hardware to default values, allocate the
- appropriate mixer component and create any other memory that is required.
-
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __InitOutputDevice(SoundComponentGlobalsPtr globals, long actions) {
- #pragma unused (actions)
- ComponentResult result;
-
- #if DEBUG
- DebugStr ("\pIn __InitOutputDevice;g");
- #endif
-
- // make sure we got our globals
- if (globals->hwGlobals != nil) {
- // setup the hardware
- result = SetupHardware(globals);
- if (result == noErr) {
- // first create a mixer and tell it the type of data it should output. The
- // description includes sample format, sample rate, sample size, number of channels
- // and the size of your optimal interrupt buffer. If a mixer cannot be found that
- // will output this type of data, an error will be returned.
-
- result = OpenMixerSoundComponent(&globals->thisSifter, 0, &globals->sourceComponent);
- } else {
- result = result;
- }
- } else {
- result = notEnoughHardwareErr;
- }
-
- #if DEBUG
- if (result != noErr) {
- DebugStr ("\pGot an error in __InitOutputDevice");
- }
- #endif
-
- return (result);
- }
-
- /* ==============================================================================
- SoundComponentGetInfo
-
- This routine returns information about this component to the Sound Manager. A
- 4-byte OSType selector is used to determine the type and size of the information
- to return. If the component does not support a selector, it should delegate this
- call on up the component chain.
-
- NOTE: This can be called at interrupt time. However, selectors that return
- a handle will not be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __SoundComponentGetInfo(SoundComponentGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr) {
- HandleListPtr listPtr;
- short *sp,
- i;
- UnsignedFixed *lp;
- Handle h;
- HardwareGlobalsPtr hwGlobals = globals->hwGlobals;
- ComponentResult result = noErr;
-
- #if DEBUG
- char sel[5];
- DebugStr ("\pIn __SoundComponentGetInfo;g");
- sel[0] = 4;
- BlockMoveData (&selector, &(sel[1]), 4);
- #endif
-
- // make sure we got our globals
- if (hwGlobals != nil) {
- switch (selector) {
- case siSampleSize: // return current sample size
- *((short *) infoPtr) = hwGlobals->sampleSize;
- break;
-
- case siSampleSizeAvailable: // return samples sizes available
- h = NewHandle(sizeof(short) * kSampleSizesCount); // space for sample sizes
- if (h != nil) {
- listPtr = (HandleListPtr) infoPtr;
- listPtr->count = 0; // no. sample sizes in handle
- listPtr->handle = h; // handle to be returned
-
- sp = (short *) *h; // store sample sizes in handle
-
- for (i = 0; i < kSampleSizesCount; ++i) {
- if (hwGlobals->sampleSizesActive[i]) {
- listPtr->count++;
- *sp++ = hwGlobals->sampleSizes[i];
- }
- }
- } else {
- result = MemError();
- }
- break;
-
- case siSampleRate: // return current sample rate
- *((Fixed *) infoPtr) = hwGlobals->sampleRate;
- break;
-
- case siSampleRateAvailable: // return sample rates available
- h = NewHandle(sizeof(UnsignedFixed) * kSampleRatesCount); // space for sample rates
- if (h != nil) {
- listPtr = (HandleListPtr) infoPtr;
- listPtr->count = 0; // no. sample rates in handle
- listPtr->handle = h; // handle to be returned
-
- lp = (UnsignedFixed *) *h;
-
- // If the hardware can support a range of sample rate values, then the
- // list count should be set to zero and the min and max sample rate values
- // should be stored in the handle.
-
- if (hwGlobals->supportsRateRange) {
- *lp++ = hwGlobals->sampleRateMin;
- *lp++ = hwGlobals->sampleRateMax;
- } else {
- // If the hardware supports a limited set of sample rates, then the list count
- // should be set to the number of sample rates and this list of rates should be
- // stored in the handle.
- for (i = 0; i < kSampleRatesCount; ++i) {
- if (hwGlobals->sampleRatesActive[i]) {
- listPtr->count++;
- *lp++ = hwGlobals->sampleRates[i];
- }
- }
- }
- } else {
- result = MemError();
- }
- break;
-
- case siNumberChannels: // return current no. channels
- *((short *) infoPtr) = hwGlobals->numChannels;
- break;
-
- case siChannelAvailable: // return channels available
- h = NewHandle(sizeof(short) * kChannelsCount); // space for channels
- if (h != nil) {
- listPtr = (HandleListPtr) infoPtr;
- listPtr->count = 0; // no. channels in handle
- listPtr->handle = h; // handle to be returned
-
- sp = (short *) *h; // store channels in handle
-
- for (i = 0; i < kChannelsCount; ++i) {
- if (hwGlobals->channelsActive[i]) {
- listPtr->count++;
- *sp++ = hwGlobals->channels[i];
- }
- }
- } else {
- result = MemError();
- }
- break;
-
- case siHardwareVolume:
- *((long *)infoPtr) = hwGlobals->volume;
- break;
-
- // if you do not handle this selector, then delegate it up the chain
- default:
- result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
- break;
- }
- } else {
- result = notEnoughHardwareErr;
- }
-
- #if DEBUG
- if (result != noErr) {
- DebugStr ("\pGot an error in __SoundComponentGetInfo, selector was: ;g");
- DebugStr ((unsigned char*)sel);
- }
- #endif
-
- return (result);
- }
-
- /* ==============================================================================
- SoundComponentSetInfo
-
- This routine sets information about this component. A 4-byte OSType selector is
- used to determine the type and size of the information to apply. If the component
- does not support a selector, it should delegate this call on up the component chain.
-
- NOTE: This can be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __SoundComponentSetInfo(SoundComponentGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr) {
- HardwareGlobalsPtr hwGlobals = globals->hwGlobals;
- ComponentResult result = noErr;
- short i;
-
- #if DEBUG
- DebugStr ("\pIn __SoundComponentSetInfo;g");
- #endif
-
- // make sure we got our globals
- if (hwGlobals != nil) {
- switch (selector) {
- case siSampleSize: // set sample size
- {
- short sampleSize = (short) infoPtr;
-
- result = siInvalidSampleSize;
- for (i = kSampleSizesCount - 1; i >= 0; --i) {
- if ((hwGlobals->sampleSizesActive[i]) &&
- (hwGlobals->sampleSizes[i] == sampleSize)) {
- hwGlobals->sampleSize = sampleSize;
- hwGlobals->dirty = true;
- result = noErr;
- break;
- }
- }
- break;
- }
-
- case siSampleRate: // set sample rate
- {
- UnsignedFixed sampleRate = (UnsignedFixed) infoPtr;
-
- if (hwGlobals->supportsRateRange) {
- if ((hwGlobals->sampleRateMin <= sampleRate) && (sampleRate <= hwGlobals->sampleRateMax)) {
- hwGlobals->sampleRate = sampleRate;
- hwGlobals->dirty = true;
- } else {
- result = siInvalidSampleRate;
- }
- } else {
- result = siInvalidSampleRate;
- for (i = kSampleRatesCount - 1; i >= 0; --i) {
- if ((hwGlobals->sampleRatesActive[i]) &&
- (hwGlobals->sampleRates[i] == sampleRate)) {
- hwGlobals->sampleRate = sampleRate;
- hwGlobals->dirty = true;
- result = noErr;
- break;
- }
- }
- }
- break;
- }
-
- case siNumberChannels: // set no. channels
- {
- short numChannels = (short) infoPtr;
-
- result = notEnoughHardware;
- for (i = kChannelsCount - 1; i >= 0; --i) {
- if ((hwGlobals->channelsActive[i]) &&
- (hwGlobals->channels[i] == numChannels)) {
- hwGlobals->numChannels = numChannels;
- hwGlobals->dirty = true;
- result = noErr;
- break;
- }
- }
- break;
- }
-
- case siHardwareVolume:
- DebugStr ("\pin siHardwareVolume");
- hwGlobals->volume = (long) infoPtr;
- hwGlobals->dirty = true;
- break;
-
- // if you do not handle this selector, then call up the chain
- default:
- result = SoundComponentSetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
- break;
- }
- } else {
- result = notEnoughHardwareErr;
- }
-
- #if DEBUG
- if (result != noErr) {
- DebugStr ("\pGot an error in __SoundComponentSetInfo");
- }
- #endif
-
- return (result);
- }
-
- /* ==============================================================================
- StartSource
-
- This routine is used to start sounds playing that are currently paused. It should
- first delegate this call up the component chain so the rest of the chain can prepare
- to play this sound. Then, if the hardware is not already started it should be
- turned on.
-
- NOTE: This can be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __StartSource(SoundComponentGlobalsPtr globals, short count, SoundSource *sources) {
- ComponentResult result;
-
- #if DEBUG
- DebugStr ("\pIn __StartSource;g");
- #endif
-
- // tell the mixer to start these sources
- result = SoundComponentStartSource(globals->sourceComponent, count, sources);
- if (result == noErr) {
- // make sure hardware interrupts are running
- StartHardware(globals);
- }
-
- #if DEBUG
- if (result != noErr) {
- DebugStr ("\pGot an error in __StartSource");
- }
- #endif
-
- return (result);
- }
-
- /* ==============================================================================
- PlaySourceBuffer
-
- This routine is used to specify a new sound to play and conditionally start
- the hardware playing that sound. It should first delegate this call up the component
- chain so the rest of the chain can prepare to play this sound. Then, if the
- hardware is not already started it should be turned on.
-
- NOTE: This can be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __SoundComponentPlaySourceBuffer(SoundComponentGlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions) {
- ComponentResult result;
-
- #if DEBUG
- DebugStr ("\pIn __SoundComponentPlaySourceBuffer;g");
- #endif
-
- // tell mixer to start playing this new buffer
- result = SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions);
- if (result == noErr) {
- // if the kSourcePaused bit is set, then do not turn on your hardware just yet
- // (the assumption is that StartSource() will later be used to start this sound playing).
- // If this bit is not set, turn your hardware interrupts on.
-
- if (!(actions & kSourcePaused)) {
- StartHardware(globals);
- }
- }
-
- #if DEBUG
- if (result != noErr) {
- DebugStr ("\pGot an error in __SoundComponentPlaySourceBuffer");
- }
- #endif
-
- return (result);
- }
-